'Master server protocol handler.'
import struct, cPickle, time
import accumulator
import neighbours
import socket
import time
import random
import conf

CHECK_NODE_HOSTNAMES = False

if not CHECK_NODE_HOSTNAMES: print 'Warning: CHECK_NODE_HOSTNAMES not set.'

# hostname lookup table to serve as a backup for when DNS lookups fail
ip2hostname = {}
def update_ip2hostname():
    print 'Rebuilding IP lookup table from',len(neighbours.nodes),'nodes.'
    start = time.time()   
    for node in neighbours.nodes:
        try:
            ip2hostname[socket.gethostbyname(node)] = node
        except:
            print '\tEgregious IP lookup failure for node',node
    print 'Done. Update took a total of',(time.time()-start),'seconds.'


def handler(data, addr):
    id,data = ord(data[:1]),data[1:]
    # get hostname
    hostname,port = addr
    hostname = socket.getfqdn(hostname).lower() # look up plain ip addresses
    # check if DNS lookup succeeded
    if hostname not in neighbours.nodes:
        # uh crud - DNS lookup failed, we're stuck with a raw IP
        if hostname not in ip2hostname:
            # no idea: brute force lookup ahoy!
            update_ip2hostname()
        if hostname in ip2hostname:
            # we know what the hostname is though
            # because we looked it up in the past
            print ('DNS lookup failed for IP',hostname,
                    'but it was found in the ip2hostname cache as',
                     ip2hostname[hostname])
            hostname = ip2hostname[hostname]
        else:
            # looks like our own lookup attempt failed
            # give up, but make it clear a failure occured!
            print 'Could not locate hostname',hostname,'in system.'
            hostname = "UNKNOWN:"+hostname


    # data-push
    if id is 1: 
        # get serialized dictionaries
        tup = cPickle.loads(data)
        node_time,stats,nodes = tup
        # get timestamp, and calculate approx. difference from server
        my_time = time.time()
        time_delta = node_time-my_time # positive = node is 'ahead'
        print 'Statistics received:',stats
        print 'Node verticality received:',nodes
        print 'Node time:',node_time,'\nMy time:',my_time,'\nDelta:',time_delta
        # add new information
        stats['clock_delta'] = time_delta
        stats['last_heard'] = time.strftime('%b %d %H:%M:%S', 
                                                        time.gmtime(my_time))
        # if we never heard from this node before, make sure it sends us all 
        # data next tick
        if hostname not in accumulator.nodes:
            do_clear_cache = 1
        else:
            do_clear_cache = 0
        accumulator.add(hostname, stats, nodes)
        # [4 byte node list timestamp] (little endian)
        # [4 byte version number] 
        # [1 byte 1/0 True/False 'reset cache' flag] (probably won't be needed)
        return '\1'+struct.pack('<IIB', neighbours.stamp, conf.VERSION,
                                                             do_clear_cache)
    elif id is 2:
        i = random.randrange(0,len(neighbours.nodes)) 
        # randrange doesn't include upper bound
        try:
            i = neighbours.nodes.index(hostname.lower())
        except:
            print 'Node not on list tried to request node list:',hostname
            if CHECK_NODE_HOSTNAMES:
                return '\2nope'
        wrap_around = neighbours.nodes[i+1:]+neighbours.nodes[:i]
        return '\2'+cPickle.dumps((neighbours.stamp,wrap_around))
    else:
        raise RuntimeError('Invalid request received from '+str(addr))

